package main

import (
	"archive/zip"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/fsnotify/fsnotify"
)

var (
	processingFiles = make(map[string]bool)
	mu              sync.Mutex
)

func main() {
	targetDir := flag.String("dir", ".", "Directory to monitor")
	flag.Parse()

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}
	defer watcher.Close()

	// 初始全量扫描
	go initialScan(*targetDir)

	// 添加定时扫描（每小时一次兜底）
	go scheduleScan(*targetDir)

	// 实时监听文件变化
	go watchEvents(watcher)

	err = watcher.Add(*targetDir)
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("当前正在监控的目录: %s", *targetDir)
	select {} // 永久阻塞
}

func initialScan(dir string) {
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if !info.IsDir() && filepath.Ext(path) != ".zip" {
			checkAndCompress(path)
		}
		return nil
	})
}

func scheduleScan(dir string) {
	ticker := time.NewTicker(1 * time.Hour)
	for range ticker.C {
		initialScan(dir)
	}
}

func watchEvents(watcher *fsnotify.Watcher) {
	eventTimer := make(map[string]*time.Timer)
	eventLock := sync.Mutex{}

	for {
		select {
		case event, ok := <-watcher.Events:
			if !ok {
				return
			}
			if event.Op&(fsnotify.Write|fsnotify.Create) != 0 {
				eventLock.Lock()
				if timer, exists := eventTimer[event.Name]; exists {
					timer.Stop()
				}
				// 防抖处理：事件触发后等待 10 秒再处理
				eventTimer[event.Name] = time.AfterFunc(10*time.Second, func() {
					checkAndCompress(event.Name)
					eventLock.Lock()
					delete(eventTimer, event.Name)
					eventLock.Unlock()
				})
				eventLock.Unlock()
			}

		case err, ok := <-watcher.Errors:
			if !ok {
				return
			}
			log.Println("Watcher error:", err)
		}
	}
}

func checkAndCompress(filePath string) {
	if filepath.Ext(filePath) == ".zip" {
		return
	}

	mu.Lock()
	if processingFiles[filePath] {
		mu.Unlock()
		return
	}
	processingFiles[filePath] = true
	mu.Unlock()

	defer func() {
		mu.Lock()
		delete(processingFiles, filePath)
		mu.Unlock()
	}()

	fileInfo, err := os.Stat(filePath)
	if err != nil {
		if os.IsNotExist(err) {
			return
		}
		log.Printf("Stat error: %v", err)
		return
	}

	if fileInfo.Size() > 2*1024*1024 { // 2MB
		timestamp := time.Now().Format("20060102150405")
		zipPath := fmt.Sprintf("%s_%s.zip", filePath, timestamp)

		if err := compressFile(filePath, zipPath); err != nil {
			log.Printf("Compress failed: %v", err)
			return
		}

		log.Printf("Compressed: %s -> %s", filePath, zipPath)
	}
}

func compressFile(src, dst string) error {
	srcFile, err := os.Open(src)
	if err != nil {
		return err
	}
	defer srcFile.Close()

	zipFile, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer zipFile.Close()

	zipWriter := zip.NewWriter(zipFile)
	defer zipWriter.Close()

	info, err := srcFile.Stat()
	if err != nil {
		return err
	}

	header, err := zip.FileInfoHeader(info)
	if err != nil {
		return err
	}
	header.Name = filepath.Base(src)
	header.Method = zip.Deflate

	writer, err := zipWriter.CreateHeader(header)
	if err != nil {
		return err
	}

	_, err = io.Copy(writer, srcFile)
	return err
}
